package socket;

import java.awt.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class Server {
    private ServerSocket serverSocket;

    private List <PrintWriter> allOut = new ArrayList<>();


    public Server() {
        /*
                创建ServeSocket时要指定服务端口,该端口不能与服务端所在
                计算机上其它应用程序开启的端口重复,否则会抛出异常:
                java.net.BindException:address already in use
                解决办法:
                1:更换端口,直到可用
                2:杀死使用该端口的进程(程序),仅在咱们重复启动服务端导致时使用
             */

        try {
            System.out.println("正在启动服务端");
            serverSocket = new ServerSocket(8088);
            System.out.println("服务端启动完毕!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public void start() {
        try {
            while (true) {
                System.out.println("等待客户端连接");
                /*
                    ServerSocket的accept方法用于等待客户端的连接.
                    该方法是一个阻塞方法,直到一个客户端建立连接,此时该方法会立即
                    返回一个Socket实例,使用该Socket实例即可与对方交互.
                 */
                Socket socket = serverSocket.accept();
                System.out.println("一个客户端连接了");

                //启动一个线程来处理与该客户端的交互
                ClientHandler handler = new ClientHandler(socket);
                Thread t = new Thread(handler);
                t.start();


            }
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }

    private class ClientHandler implements Runnable{
        private Socket socket;
        private String host;//记录客户端的IP地址
        public ClientHandler(Socket socket){
            this.socket =socket;
            //通过socket获取远程计算机地址
            host = socket.getInetAddress().getHostAddress();

        }
        public void run() {
            PrintWriter pw = null;
            try {
                InputStream in = socket.getInputStream();
                InputStreamReader isr
                        = new InputStreamReader(in, StandardCharsets.UTF_8);
                BufferedReader br = new BufferedReader(isr);

                //通过socket获取输出流用来将消息发送给远程计算机(客户端)
                OutputStream out = socket.getOutputStream();
                OutputStreamWriter osw =
                        new OutputStreamWriter(out,StandardCharsets.UTF_8);
                BufferedWriter bw = new BufferedWriter(osw);
                pw = new PrintWriter(bw,true);
                synchronized (allOut) {
                    allOut.add(pw);
                }
                sendMessage(host+"上线了,当前在线人数:"+allOut.size());


                //读取一行来自远端计算机发送过来的字符串
                String message;
                while ((message = br.readLine()) != null) {
                    System.out.println(host +"说:"+ message);
                    sendMessage(host +"说:"+ message);
                    //将消息回复给客户端
                    pw.println(host +"说:"+message);

                }
            } catch (IOException e) {

            }finally {
                //处理客户端断开链接后的操作
                //从allOut中将该客户端的输出流删除
                synchronized (allOut) {
                    allOut.remove(pw);
                }
                //广播下线通知
                sendMessage(host+"下线了,当前在线人数:"+allOut.size());
                //关闭socket释放资源
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //将消息广播给所有客户端
        private void sendMessage(String message){
            synchronized (allOut) {
                for (PrintWriter o : allOut){
                    o.println(message);
                    System.out.println(message);
                }
            }
        }
    }
}
